﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Runtime.Serialization.Formatters.Binary;

namespace DELVE_GAME
{
    class NetworkedGameClient : Game
    {
        TcpClient client;// = new TcpClient();
        BinaryFormatter bf;
        NetworkStream ns;

        List<Turn> pendingTurns;
        int localPlayer;

        public NetworkedGameClient(GameSettings gameSettings, string hostIPAddress)
            : base(gameSettings) 
        {
            pendingTurns = new List<Turn>();
            initializeConnection(hostIPAddress);
        }

        public void initializeConnection(string hostIPAddress)
        {

            client = new TcpClient();
            client.Connect(new IPEndPoint(IPAddress.Parse(hostIPAddress), 5005));

            bf = new BinaryFormatter();
            ns = client.GetStream();
        }

        //Sends turn data to other players
        public override void endTurn()
        {
            //Turn toSubmit = gameLog.getLastTurn();
            gameLog.addAction((int)ACTIONS.END_TURN);
            if (!player.isAlive())
            {
                /*
                 * drop out of this computer's personal game
                 * this means that if we allow 3+ players,
                 * you'll be stuck waiting!
                 */
                eliminatePlayer(player);
            }
            else
            {
                //send to all other players
                //sendTurn();
                receiveTurn();
                sendTurn();
                gameOver = changeTurn();
            }
            base.endTurn();
        }

        //Send turn to all other players
        private void sendTurn()
        {
            Turn toSubmit = gameLog.getLastTurn();


            //Turn myT = new Turn();
            //List<string> l = new List<string>() { "Lorem", "ipsum", "dolor", "sit", "amet" };
            if (ns.CanWrite) bf.Serialize(ns, toSubmit);
            //bf.Serialize(ns, l);
            //ns.Close();

            //client.Close();

            //127.0.0.1 for localhost
            //192.168.0.1 for router
        }

        public override bool isGameOver()
        {
            bool gameOver = base.isGameOver();
            if (gameOver)
            {
                ns.Dispose();
                client.Close();

            }
            return gameOver;//base.isGameOver();
        }

        public void receiveGameData()
        {
            //GameSettings myGS = new GameSettings();
            //myGS =
            this.gameSettings = (GameSettings)bf.Deserialize(ns);

            //this.gameSettings = gameSettings;
            initializeGame(new Board((Map)bf.Deserialize(ns)));
            readOnly = false;

            gameLog = new GameLog();
            gameLog.addSettings(gameSettings);

            localPlayer = 1;
            this.currentPlayer = localPlayer;
            player = players[currentPlayer];
            startTurn();

        }

        /*
         * Receive turn from another player
         * Return true if this ends the game
         */
        public void receiveTurn()
        {

            Turn storedTurn = new Turn();
            while (client.Connected && !ns.DataAvailable) ;
            if (client.Connected) storedTurn = (Turn)bf.Deserialize(ns);

            /*
            bool addTurn = true;
            foreach(Turn storedTurn in pendingTurns) 
            {
                if(storedTurn.playerIndex == turn.playerIndex) 
                {
                    addTurn = false;
                    break;
                }
            }
             */

            pendingTurns.Add(storedTurn);
        }

        public override bool changeTurn()
        {
            bool gameOver = false;
            if (pendingTurns.Count == stillAlive.Count - 1)
            {
                while (pendingTurns.Count > 0)
                {
                    doTurn(pendingTurns[0]);
                    pendingTurns.RemoveAt(0);
                }
                barrier();
                foreach (Player p in players)
                {
                    if (!p.isAlive() && !stillAlive.Contains(p) &&
                        p.getPlayerIndex() != localPlayer)
                        //Local player isn't eliminated until after they get to see that they're gone
                        eliminatePlayer(p);
                }
                currentPlayer = localPlayer;
                player = players[currentPlayer];
                this.turn++;
                startTurn();
                gameOver = isGameOver();
            }
            return isGameOver();
        }

        private void doTurn(Turn turn)
        {
            currentPlayer = turn.playerIndex;
            player = players[currentPlayer];
            Tile tile;
            int playerIndex;
            foreach (Action action in turn.actions)
            {
                switch (action.getActionIndex())
                {
                    case (int)ACTIONS.PLACE_BASES:
                        tile = board.getLocation(action.getLocation());
                        doMove(tile);
                        break;
                    case (int)ACTIONS.UPGRADE:
                        tile = board.getLocation(action.getLocation());
                        int upgrade = action.getUpgradeType();
                        applyUpgrade(tile, upgrade);
                        break;
                    case (int)ACTIONS.SEND_REQUEST:
                        playerIndex = action.getPlayerIndex();
                        sendAllianceRequest(playerIndex);
                        break;
                    case (int)ACTIONS.ACCEPT_REQUEST:
                        playerIndex = action.getPlayerIndex();
                        respondToRequest(playerIndex, true);
                        break;
                    case (int)ACTIONS.BREAK_ALLIANCE:
                        playerIndex = action.getPlayerIndex();
                        breakAlliance(playerIndex);
                        break;
                    case (int)ACTIONS.END_TURN:
                        break;
                }
            }
        }
    }
}